<?php

/*
 * Copyright (C) 2009 - 2011 Pham Cong Dinh
 *
 * This file is part of Spica.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

/**
 * Provides methods for linking to produce paginated data.
 *
 * @category   spica
 * @package    core
 * @subpackage datasource
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      July 10, 2009
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Paginator.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaPaginator
{
    /**
     * Database access object.
     *
     * @var SpicaConnection
     */
    private $_ds;

    /**
     * Page to fetch.
     *
     * @var int
     */
    private $_page;

    /**
     * Number of items to be fetched per page.
     *
     * @var int
     */
    private $_itemPerPage;

    /**
     * The query to fetch total rows.
     *
     * @var string
     */
    private $_totalRowsQuery;

    /**
     * The query to fetch page specific data.
     *
     * @var string
     */
    private $_paginatedQuery;

    /**
     * Holds named parameters and their binding values, including 2 keys: <code>total_rows</code> and <code>paginated</code>
     *
     * @var array
     */
    private $_bind = array();

    /**
     * Constructs an object of <code>SpicaPaginator</code>.
     *
     * @param SpicaConnection $connection
     * @param int $page The page to fetch data from
     * @param int $itemPerPage The number of records to be fetched per page
     */
    public function __construct(SpicaConnection $connection, $page, $itemPerPage)
    {
        $this->_ds          = $connection;
        $this->_page        = (int) $page;
        $this->_itemPerPage = (int) $itemPerPage;
    }

    /**
     * Sets query to fetch total rows.
     *
     * @param string $query
     * @param array  $bind For example: array(':name_like' => $like)
     */
    public function setTotalRowsQuery($query, $bind = null)
    {
        $this->_totalRowsQuery = $query;

        if (true === is_array($bind))
        {
            $this->_bind['total_rows'] = $bind;
        }
    }

    /**
     * Sets query to fetch page specific data.
     *
     * @param string $query
     * @param array  $bind For example: array(':name_like' => $like)
     */
    public function setPaginatedQuery($query, $bind = null)
    {
        $this->_paginatedQuery = $query;

        if (true === is_array($bind))
        {
            $this->_bind['paginated'] = $bind;
        }
    }

    /**
     * Executes the queries and produces paginated data.
     *
     * @throws Exception if there is any wrong with database connection or query execution
     * @return array Contains 2 keys: <code>total_rows</code> and <code>data</code>
     */
    public function execute()
    {
        $data = array();

        if (null === $this->_totalRowsQuery)
        {
            $q = str_replace(array("\n", "\r"), '', $this->_paginatedQuery);
            $this->_totalRowsQuery = 'SELECT COUNT(1) AS num_rows'.substr($q, stripos($q, ' FROM '));
        }

        // Binds named placeholder
        if (true === isset($this->_bind['total_rows']))
        {
            $stmt = $this->_ds->prepareStatement($this->_totalRowsQuery);
            $stmt->bindParams($this->_bind['total_rows']);
            $data['total_rows'] = (int) $stmt->fetchOne();
            $stmt->close();

            // If there is no row
            if (0 === $data['total_rows'])
            {
                return array();
            }

            if (true === isset($this->_bind['paginated']))
            {
                $stmt = $this->_ds->prepareStatement($this->_paginatedQuery);
                $stmt->bindParams($this->_bind['paginated']);
                $data['data'] = $stmt->fetchPage($this->_page, $this->_itemPerPage);
            }
        }
        else
        {
            $stmt = $this->_ds->createStatement();
            $data['total_rows'] = (int) $stmt->fetchOne($this->_totalRowsQuery);
            $data['data'] = $stmt->fetchPage($this->_paginatedQuery, $this->_page, $this->_itemPerPage);
        }  

        $stmt->close();
        return $data;
    }
}

?>